package com.easyaop.asm.bytecode.asm;

import com.easyaop.asm.bytecode.*;
import com.easyaop.asm.bytecode.asm.methodvisitor.DefaultMethodVisitorCompile;
import lombok.Setter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import java.util.LinkedList;
import java.util.List;

public class AroundClassVisitor extends ClassVisitor {
    @Setter
    private List<MethodInfo> methodInfoList;
    @Setter
    private DefaultMethodVisitorCompile around;
    @Setter
    private int jpPos;

    public AroundClassVisitor(int api, ClassVisitor classVisitor) {
        super(api, classVisitor);
    }

    @Override
    public void visitEnd() {
        for (MethodInfo target : methodInfoList) {
            add(target);
        }
        super.visitEnd();
    }

    private void add(MethodInfo target) {
        MethodVisitor methodVisitor = super.visitMethod(target.access, target.name, target.descriptor, target.signature, target.exceptions);
//      切面里的位置，偏移量
        int offset = -1 // 切面里有个this，前移一个位置
                + target.argSize();
        offset=target.maxLocal
                -1// 切面里有this，前移一个位置
        ;


        RetAroundMethodVisitor retAroundMethodVisitor = new RetAroundMethodVisitor(api, methodVisitor);
        retAroundMethodVisitor.setTarget(target);
        methodVisitor = retAroundMethodVisitor;

        JoinMethodVisitor joinMethodVisitor = new JoinMethodVisitor(api, methodVisitor);
        joinMethodVisitor.setTarget(target);
        joinMethodVisitor.setOffSet(offset);// 切面的位置全部调整(调整到业务方法的入参数之后)
        joinMethodVisitor.setJpPos(jpPos);
        methodVisitor = joinMethodVisitor;


        List<TryVo> aopTry=new LinkedList<>();
        List<TryVo> targetTry=new LinkedList<>();


        MethodVisitorWriteTry writeTry2 = new MethodVisitorWriteTry(api, methodVisitor);
        writeTry2.trys =aopTry;
        methodVisitor=writeTry2;

        MethodVisitorWriteTry writeTry1 = new MethodVisitorWriteTry(api, methodVisitor);
        writeTry1.trys =targetTry;
        methodVisitor=writeTry1;

        MethodVisitorDelTry visitorDelTry = new MethodVisitorDelTry(api, methodVisitor);
        visitorDelTry.trys =aopTry;
        methodVisitor=visitorDelTry;

        CallMethodVisitor callMethodVisitor = new CallMethodVisitor(api, methodVisitor);
        callMethodVisitor.setTarget(target);
        callMethodVisitor.setOffSet(offset);
        callMethodVisitor.tryVos =targetTry;
        methodVisitor = callMethodVisitor;



//            -------
        OffsetMethodVisitor offsetMethodVisitor = new OffsetMethodVisitor(api, methodVisitor);
        offsetMethodVisitor.setOffset(offset);
        methodVisitor = offsetMethodVisitor;

        methodVisitor = new ThisLocalVar(api, methodVisitor);
        methodVisitor = new RmParams(api, methodVisitor);

        methodVisitor = new MethodVisitor(api, methodVisitor) {
            public void visitVarInsn(int opcode, int varIndex) {
                if (varIndex == 0) {
                    throw new RuntimeException("切面里不能访问this");
                }
                super.visitVarInsn(opcode, varIndex);
            }
        };

        around.execute(methodVisitor);
    }

    private static class ThisLocalVar extends MethodVisitor {

        protected ThisLocalVar(int api, MethodVisitor methodVisitor) {
            super(api, methodVisitor);
        }

        public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
            if (index == 0) {
                return;
            }
            super.visitLocalVariable(name, descriptor, signature, start, end, index);
        }
    }
    private static class RmParams extends MethodVisitor {

        protected RmParams(int api, MethodVisitor methodVisitor) {
            super(api, methodVisitor);
        }

        @Override
        public void visitParameter(String name, int access) {
        }
    }

}
